package logger

import (
	"log"
	"os"
	"strings"
	"time"

	rotatelogs "github.com/lestrrat-go/file-rotatelogs"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

func initZapLevel(level string) (zapLevel zap.AtomicLevel, err error) {
	zapLevel = zap.NewAtomicLevel()
	if level == "off" {
		zapLevel.Enabled(zap.FatalLevel)
	} else {
		err = zapLevel.UnmarshalText([]byte(level))
	}
	return zapLevel, err
}

func initZap(config Config, level zap.AtomicLevel) *zap.Logger {
	encoder := zapcore.NewJSONEncoder(
		zapcore.EncoderConfig{
			MessageKey:     "message",
			LevelKey:       "level",
			TimeKey:        "time",
			NameKey:        "name",
			CallerKey:      "caller",
			StacktraceKey:  "stacktrace",
			LineEnding:     zapcore.DefaultLineEnding,
			EncodeLevel:    zapcore.CapitalLevelEncoder,
			EncodeTime:     TimeEncoder,
			EncodeDuration: zapcore.StringDurationEncoder,
			EncodeCaller:   zapcore.ShortCallerEncoder,
			EncodeName:     zapcore.FullNameEncoder,
		})

	core := zapcore.NewTee(getCores(config, encoder, level)...)
	return zap.New(core)
}

func getCores(config Config, encoderCfg zapcore.Encoder, zapLevel zap.AtomicLevel) (cores []zapcore.Core) {
	defer func() {
		if len(cores) == 0 {
			cores = append(cores, zapcore.NewCore(encoderCfg, os.Stdout, zapLevel))
		}
	}()
	if config.IsConsole {
		cores = append(cores, zapcore.NewCore(encoderCfg, os.Stdout, zapLevel))
	}
	if config.LogFile == "" {
		return cores
	}
	cores = append(cores, zapcore.NewCore(encoderCfg, getWriteSyncer(config), zapLevel))
	return cores
}

func getWriteSyncer(config Config) zapcore.WriteSyncer {
	if strings.ToLower(config.LogType) == rollTypeTimeDaily {
		writer, err := rotatelogs.New(
			config.LogFile+".%Y%m%d",
			rotatelogs.WithLinkName(config.LogFile),
			rotatelogs.WithRotationTime(time.Hour*24),
		)
		if err != nil {
			log.Fatal(err)
		}
		return zapcore.AddSync(writer)
	} else if strings.ToLower(config.LogType) == rollTypeFileSize {
		size, err := ToGBSize(config.LogUnit, config.LogSize)
		if err != nil {
			log.Fatal(err)
		}
		hook := &lumberjack.Logger{
			Filename:   config.LogFile, //filePath
			MaxSize:    size,           // megabytes
			MaxBackups: int(config.LogMaxNum),
		}
		return zapcore.AddSync(hook)
	}
	file, _, err := zap.Open(config.LogFile)
	if err != nil {
		log.Fatal(err)
	}
	return file
}

func TimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
	layout := "2006-01-02 15:04:05.999999"
	type appendTimeEncoder interface {
		AppendTimeLayout(time.Time, string)
	}
	if enc, ok := enc.(appendTimeEncoder); ok {
		enc.AppendTimeLayout(t, layout)
		return
	}
	enc.AppendString(t.Format(layout))
}
